#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pd_linux.h"
#include "config_ch34x.h"
#include "libusb.h"
#include "xlibusb.h"
namespace config_ch34x
{
    const int CH341_REQTYPE_HOST_FROM_DEVICE = LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN;
    const int CH341_REQTYPE_HOST_TO_DEVICE = LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT;

    const int CH341_REQ_WRITE_REG = 0x9A;
    const int CH341_REQ_READ_REG = 0x95;
    const int CH341_REG_BREAK1 = 0x05;
    const int CH341_REG_BREAK2 = 0x18;
    const int CH341_NBREAK_BITS_REG1 = 0x01;
    const int CH341_NBREAK_BITS_REG2 = 0x40;

    // Baud rates values
    const int CH34X_300_1312 = 0xd980;
    const int CH34X_300_0f2c = 0xeb;

    const int CH34X_600_1312 = 0x6481;
    const int CH34X_600_0f2c = 0x76;

    const int CH34X_1200_1312 = 0xb281;
    const int CH34X_1200_0f2c = 0x3b;

    const int CH34X_2400_1312 = 0xd981;
    const int CH34X_2400_0f2c = 0x1e;

    const int CH34X_4800_1312 = 0x6482;
    const int CH34X_4800_0f2c = 0x0f;

    const int CH34X_9600_1312 = 0xb282;
    const int CH34X_9600_0f2c = 0x08;

    const int CH34X_19200_1312 = 0xd982;
    const int CH34X_19200_0f2c_rest = 0x07;

    const int CH34X_38400_1312 = 0x6483;

    const int CH34X_57600_1312 = 0x9883;

    const int CH34X_115200_1312 = 0xcc83;

    const int CH34X_230400_1312 = 0xe683;

    const int CH34X_460800_1312 = 0xf383;

    const int CH34X_921600_1312 = 0xf387;

    const int CH34X_1228800_1312 = 0xfb03;
    const int CH34X_1228800_0f2c = 0x21;

    const int CH34X_2000000_1312 = 0xfd03;
    const int CH34X_2000000_0f2c = 0x02;

    // Parity values
    const int CH34X_PARITY_NONE = 0xc3;
    const int CH34X_PARITY_ODD = 0xcb;
    const int CH34X_PARITY_EVEN = 0xdb;
    const int CH34X_PARITY_MARK = 0xeb;
    const int CH34X_PARITY_SPACE = 0xfb;

    //Flow control values
    const int CH34X_FLOW_CONTROL_NONE = 0x0000;
    const int CH34X_FLOW_CONTROL_RTS_CTS = 0x0101;
    const int CH34X_FLOW_CONTROL_DSR_DTR = 0x0202;


    const int PARITY_NONE = 0;
    const int PARITY_ODD = 1;
    const int PARITY_EVEN = 2;
    const int PARITY_MARK = 3;
    const int PARITY_SPACE = 4;

    const int FLOW_CONTROL_OFF = 0;
    const int FLOW_CONTROL_RTS_CTS = 1;
    const int FLOW_CONTROL_DSR_DTR = 2;
    const int FLOW_CONTROL_XON_XOFF = 3;

    int setControlCommandOut(void *handle, int request, int value, int index, unsigned char *data, int dataLength)
    {
        int response = x_libusb_controltransfer(handle, CH341_REQTYPE_HOST_TO_DEVICE, request, value, index, data, dataLength, 1000);
        return response;
    }

    int setControlCommandIn(void *handle, int request, int value, int index, unsigned char *data, int dataLength)
    {
        int response = x_libusb_controltransfer(handle, CH341_REQTYPE_HOST_FROM_DEVICE, request, value, index, data, dataLength, 1000);
        return response;
    }

    int checkState(void *handle, int request, int value, unsigned char *expected, int expLength)
    {
        unsigned char szBuffer[1024] = {0x0};
        int ret = setControlCommandIn(handle, request, value, 0, szBuffer, expLength);

        if(ret != expLength)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }

    int checkCTS(void *handle)
    {
        unsigned char szBuffer[2] = {0x0};
        int ret = setControlCommandIn(handle, CH341_REQ_READ_REG, 0x0706, 0, szBuffer, 2);

        if(ret != 2)
        {
            return -1;
        }

        if((szBuffer[0] & 0x01) == 0x00) //CTS ON
        {
            return 0;
        }
        else  // CTS OFF
        {
            return -2;
        }
    }

    int checkDSR(void *handle)
    {
        unsigned char szBuffer[2] = {0x0};
        int ret = setControlCommandIn(handle, CH341_REQ_READ_REG, 0x0706, 0, szBuffer, 2);
        if(ret != 2)
        {
            return -1;
        }

        if((szBuffer[0] & 0x02) == 0x00) //DSR ON
        {
            return 0;
        }
        else  // DSR OFF
        {
            return -2;
        }
    }

    int setCh340xParity(void *handle, int indexParity)
    {
        unsigned char state1[2] = {0x9f, 0xee};
        if(setControlCommandOut(handle, CH341_REQ_WRITE_REG, 0x2518, indexParity, NULL, 0) < 0)
        {
            return -1;
        }
        if(checkState(handle, 0x95, 0x0706, state1, 2) == -1)
        {
            return -1;
        }
        if(setControlCommandOut(handle, CH341_REQ_WRITE_REG, 0x2727, 0, NULL, 0) < 0)
        {
            return -1;
        }
        return 0;
    }

    int setBaudRate(void *handle, int index1312, int index0f2c)
    {
        unsigned char state1[2] = {0x9f, 0xee};
        if(setControlCommandOut(handle, CH341_REQ_WRITE_REG, 0x1312, index1312, NULL, 0) < 0)
        {
            return -1;
        }
        if(setControlCommandOut(handle, CH341_REQ_WRITE_REG, 0x0f2c, index0f2c, NULL, 0) < 0)
        {
            return -1;
        }
        if(checkState(handle, 0x95, 0x0706, state1, 2) == -1)
        {
            return -1;
        }
        if(setControlCommandOut(handle, CH341_REQ_WRITE_REG, 0x2727, 0, NULL, 0) < 0)
        {
            return -1;
        }
        return 0;
    }


    int setCh340xFlow(void *handle, int flowControl)
    {
        unsigned char state1[2] = {0x9f, 0xee};
        if(checkState(handle, 0x95, 0x0706, state1, 2) == -1)
        {
            return -1;
        }
        if(setControlCommandOut(handle, CH341_REQ_WRITE_REG, 0x2727, flowControl, NULL, 0) == -1)
        {
            return -1;
        }
        return 0;
    }


    int Init(void *handle)
    {
        if(setControlCommandOut(handle, 0xa1, 0xc29c, 0xb2b9, NULL, 0) < 0)
        {
            return -1;
        }
        if(setControlCommandOut(handle, 0xa4, 0xdf, 0, NULL, 0) < 0)
        {
            return -1;
        }
        if(setControlCommandOut(handle, 0xa4, 0x9f, 0, NULL, 0) < 0)
        {
            return -1;
        }
        unsigned char state1[2] = {0x9f, 0xee};
        if(checkState(handle, 0x95, 0x0706, state1, 2) == -1)
        {
            return -1;
        }
        if(setControlCommandOut(handle, 0x9a, 0x2727, 0x0000, NULL, 0) < 0)
        {
            return -1;
        }

        if(setControlCommandOut(handle, 0x9a, 0x1312, 0xb282, NULL, 0) < 0)
        {
            return -1;
        }

        if(setControlCommandOut(handle, 0x9a, 0x0f2c, 0x0008, NULL, 0) < 0)
        {
            return -1;
        }

        if(setControlCommandOut(handle, 0x9a, 0x2518, 0x00c3, NULL, 0) < 0)
        {
            return -1;
        }

        if(checkState(handle, 0x95, 0x0706, state1, 2) == -1)
        {
            return -1;
        }

        if(setControlCommandOut(handle, 0x9a, 0x2727, 0x0000, NULL, 0) < 0)
        {
            return -1;
        }
        return 0;
    }

    void Free(void *handle)
    {
        //resverd
    }

    void SetBaudRate(void *handle, int baudRate)
    {
        int ret = 0;
        if(baudRate <= 300)
        {
            ret = setBaudRate(handle, CH34X_300_1312, CH34X_300_0f2c); //300
        }
        else if(baudRate > 300  && baudRate <= 600)
        {
            ret = setBaudRate(handle, CH34X_600_1312, CH34X_600_0f2c); //600
        }
        else if(baudRate > 600 && baudRate <= 1200)
        {
            ret = setBaudRate(handle, CH34X_1200_1312, CH34X_1200_0f2c); //1200
        }
        else if(baudRate > 1200 && baudRate <= 2400)
        {
            ret = setBaudRate(handle, CH34X_2400_1312, CH34X_2400_0f2c); //2400
        }
        else if(baudRate > 2400 && baudRate <= 4800)
        {
            ret = setBaudRate(handle, CH34X_4800_1312, CH34X_4800_0f2c); //4800
        }
        else if(baudRate > 4800 && baudRate <= 9600)
        {
            ret = setBaudRate(handle, CH34X_9600_1312, CH34X_9600_0f2c); //9600
        }
        else if(baudRate > 9600 && baudRate <= 19200)
        {
            ret = setBaudRate(handle, CH34X_19200_1312, CH34X_19200_0f2c_rest); //19200
        }
        else if(baudRate > 19200 && baudRate <= 38400)
        {
            ret = setBaudRate(handle, CH34X_38400_1312, CH34X_19200_0f2c_rest); //38400
        }
        else if(baudRate > 38400 && baudRate <= 57600)
        {
            ret = setBaudRate(handle, CH34X_57600_1312, CH34X_19200_0f2c_rest); //57600
        }
        else if(baudRate > 57600 && baudRate <= 115200)  //115200
        {
            ret = setBaudRate(handle, CH34X_115200_1312, CH34X_19200_0f2c_rest);
        }
        else if(baudRate > 115200 && baudRate <= 230400)  //230400
        {
            ret = setBaudRate(handle, CH34X_230400_1312, CH34X_19200_0f2c_rest);
        }
        else if(baudRate > 230400 && baudRate <= 460800)  //460800
        {
            ret = setBaudRate(handle, CH34X_460800_1312, CH34X_19200_0f2c_rest);
        }
        else if(baudRate > 460800 && baudRate <= 921600)
        {
            ret = setBaudRate(handle, CH34X_921600_1312, CH34X_19200_0f2c_rest);
        }
        else if(baudRate > 921600 && baudRate <= 1228800)
        {
            ret = setBaudRate(handle, CH34X_1228800_1312, CH34X_1228800_0f2c);
        }
        else if(baudRate > 1228800 && baudRate <= 2000000)
        {
            ret = setBaudRate(handle, CH34X_2000000_1312, CH34X_2000000_0f2c);
        }
        if(ret == -1)
        {
            printf("SetBaudRate failed!");
        }
    }

    void SetParity(void *handle, int parity)
    {
        switch(parity)
        {
            case PARITY_NONE:
                setCh340xParity(handle, CH34X_PARITY_NONE);
                break;
            case PARITY_ODD:
                setCh340xParity(handle, CH34X_PARITY_ODD);
                break;
            case PARITY_EVEN:
                setCh340xParity(handle, CH34X_PARITY_EVEN);
                break;
            case PARITY_MARK:
                setCh340xParity(handle, CH34X_PARITY_MARK);
                break;
            case PARITY_SPACE:
                setCh340xParity(handle, CH34X_PARITY_SPACE);
                break;
            default:
                break;
        }
    }

    void SetFlowControl(void *handle, int flowControl)
    {
        switch(flowControl)
        {
            case FLOW_CONTROL_OFF:
                setCh340xFlow(handle, CH34X_FLOW_CONTROL_NONE);
                break;
            case FLOW_CONTROL_RTS_CTS:
                setCh340xFlow(handle, CH34X_FLOW_CONTROL_RTS_CTS);
                break;
            case FLOW_CONTROL_DSR_DTR:
                setCh340xFlow(handle, CH34X_FLOW_CONTROL_DSR_DTR);
                break;
            default:
                break;
        }
    }

    void SetDataBits(void *handle, int dataBits)
    {

    }
    void SetStopBits(void *handle, int stopBits)
    {

    }

    void SetBreak(void *handle, int state)
    {

    }

};
